# -*- coding: utf-8 -*-
"""
Created on Sun May 14 12:23:39 2017

@author: GangTimes
"""

import numpy
import primal_demo
        
def model_fun(theta,x):
    '''
    这是模型函数
    theta:系数 matrix[mx1]  m为变量数
    x:自变量 matrix[nxm]  n为样本数   m为变量数 其中第一列对应的是 1 
    return: matrix[nx1]
    '''
    rows=x.shape[0]
    temp=(theta.T * x.T).T 
    yval=[]
    
    if rows==1:
        yval.append(_sign_fun(temp[0,0],0))
        return numpy.matrix(yval)
    elif rows>1:
        for index in range(rows):
            yval.append(_sign_fun(temp[index,0],0))
        return numpy.matrix(yval).T
            

def _sign_fun(x,b):
    '''
    sign函数  符号判别函数
    x:自变量 
    b:参考变量
    '''
    if x>=b:
        return 1
    else:
        return -1

def loss_fun(theta,x,y):
    '''
    损失函数计算
    统计误分类的函数距离
    theta:系数 matrix[mx1]
    x:自变量 matrix[nxm] 第一列为1
    y:因变量 matrix[nx1]
    return:损失函数 误分类样本点的函数间隔之和取负号
    '''
    rows=y.shape[0]
    loss_sum=0
    for index in range(rows):
        temp=fun_distance(theta,x[index,:],y[index,:])
        if temp[0,0]<0:
            loss_sum=loss_sum+temp
    
    return -1*loss_sum
        
def grad_fun(x,y):
    '''
    计算损失函数的梯度
    随机梯度法 每次只是用某个误分类样本点的梯度
    x:自变量  matrix[1xm]
    y:因变量  matrix[1x1]
    return:梯度 matrix[mx1]
    '''
    return -1*(y*x).T
    
def fun_distance(theta,x,y):
    '''
     函数间隔计算       
     theta:系数 matrix[mx1]
    x:自变量 matrix[1xm] 第一列为1
    y:因变量 matrix[1x1]
    return: 函数间隔 [1x1]
    '''
    return y*(theta.T * x.T)[0,0]
  
def primal(x,y):
    '''
    PLA算法主函数
    '''
   
    rows=x.shape[0]
    cols=x.shape[1]
    theta=numpy.matrix(numpy.ones((cols,1)))
    t=0.5
    index=0
    flag=0
    loss=[]
    while(True):
        
        if index==rows:
            index=0
        if flag==rows:
            break
        
        loss.append(loss_fun(theta,x,y))
        dist=fun_distance(theta,x[index,:],y[index,0])
        flag=flag+1
        
        if dist<0:
            delta=grad_fun(x[index,:],y[index,0])
            theta=theta-t*delta
            flag=0
        
        index=index+1
    
    return [theta,loss]

if __name__=="__main__":
    primal_demo.primal_demo()
